
#include "glMeshModelFrame.h"

#include "meshdef/clustering.h"
#include "meshdef/skinning.h"
#include "meshdef/autoSkeletonBuilder.h"
#include "skelanim/skeletonLoader.h"
#include "skelanim/motionLoader.h"

#include "utilsDeformation.h"

GLMeshModelFrame::GLMeshModelFrame(QWidget* parent, const QGLWidget* shareWidget, Qt::WFlags f)
   : QGLWidget(parent, shareWidget, f), arcball(1,1) {
   
   // for keyboard use
   setFocusPolicy(Qt::StrongFocus);
   
   cursor3D = Point3(0,0,0);
   interactionMode = VIEW_MODE;
   
   //~ texFloor = 0;
   
   fovy = 40.0;
   zoomFactor = 1.0;
   meshrendermode_mask = 0x20;
   
   isProjOrtho = true; // orthographic projection
   is_lightingOn = true;
   
   viewitems_mask = 0x0; 
   viewitems_mask = viewitems_mask | MODELDEF;
   viewitems_mask = viewitems_mask | SKELETONDEF;
   viewitems_mask = viewitems_mask | KEYPOINTS;
   viewitems_mask = viewitems_mask | CONSTRAINTS;
   viewitems_mask = viewitems_mask | FLOOR;
   viewitems_mask = viewitems_mask | INFO;
   
   // Floor and shadow specificacion
   floor[0] = Point3(-4.5,-1.0, 4.5);
   floor[1] = Point3( 4.5,-1.0, 4.5);
   floor[2] = Point3( 4.5,-1.0,-4.5);
   floor[3] = Point3(-4.5,-1.0,-4.5);
   pfloor = Plane3(floor[0], floor[2], floor[1]);
   
   float l[] = {0.0, 50.0, 20.0}; // Coordinates of the light source
   float e[] = {0.0, -0.99, 0.0}; // Point of the floor plane
   float n[] = {0.0, -1.0, 0.0}; // Normal vector for the plane
   createShadowProjectionMatrix(l,e,n);
   
   currentFrame = 0;
   
   nearplane  = 1.0;  farplane  =-1.0;
   leftplane  =-1.0; rightplane = 1.0;
   bottomplane=-1.0; topplane   = 1.0;
   
   
   numberOfFrames = 0;
   
   idSelected = -1; // the selected item
   activeobject = 1; // the active object for animation purposes: n=>skeleton alone, 0..n-1=>meshes
   
   animTimer = new QTimer(this);
   tran[0] = tran[1] = tran[2] = 0.0;
   axis[0] = 0.0; axis[1] = 1.0; axis[2] = 0.0;
   angle = 0.0;
   
   editJointMode = 'T'; // L, T, R
   defMethod = "MLS_BLENDING";
   euclideanDist = false;
   
   viewBranches = false;
   viewOriginalModel = false;
   
   viewItemOff(FLOOR);
   //~ viewItemOn(SHADOW);
   rigskin_mask = 0x08;
   generateColors();
}

GLMeshModelFrame::~GLMeshModelFrame() {
   
   cout<<"::> ~GLMeshModelFrame()"<<endl;
   if (skelSource!=0)       delete skelSource;
   
   for (int i=0; i<meshes.size(); ++i)
      if (meshes[i]!=0) delete meshes[i];
   for (int i=0; i<meshesOri.size(); ++i)
      if (meshesOri[i]!=0) delete meshesOri[i];
   for (int i=0; i<motions.size(); ++i)
      if (motions[i]!=0) delete motions[i];
      
   cout<<"::> ~GLMeshModelFrame() ... "<<endl;
}

void GLMeshModelFrame::initializeGL() {
   
   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);
   glEnable(GL_LIGHT1);
   glEnable(GL_DEPTH_TEST);
   glEnable(GL_NORMALIZE);
   
   // lighting setup
   //~ GLfloat left_light_position[] =  {0.0f, 50.0f, 50.0f, 0.0f};
   //~ GLfloat right_light_position[] = {0.0f, -50.0f, 50.0f, 0.0f};
   GLfloat left_light_position[] =  {0.0f, 20.0f, 25.0f, 0.0f};
   GLfloat right_light_position[] = {0.0f, -20.0f, 25.0f, 0.0f};
   GLfloat left_diffuse_light[] =  {1.0f, 1.0f, 1.0f, 1.0f};
   GLfloat right_diffuse_light[] = {1.0f, 1.0f, 1.0f, 1.0f};
   
   glLightfv(GL_LIGHT0, GL_POSITION, left_light_position);
   glLightfv(GL_LIGHT0, GL_DIFFUSE, left_diffuse_light);
   glLightfv(GL_LIGHT1, GL_POSITION, right_light_position);
   glLightfv(GL_LIGHT1, GL_DIFFUSE, right_diffuse_light);
   
   //simple orange
   //GLfloat mat_orange_diffuse[] = {0.9, 0.45, 0.0, 1.0};
   //GLfloat mat_specular[] = {0.7, 0.6, 0.6, 1.0};
   GLfloat mat_orange_diffuse[] = {0.9, 0.45, 0.0, 1.0};
   GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
   GLfloat mat_shininess[] = {80.0};
   glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_orange_diffuse);
   glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
   glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
   
   // sky blue
   //~ GLfloat mat_orange_diffuse[] = {0.25, 0.45, 0.9, 1.0};
   //~ GLfloat mat_specular[] = {0.633, 0.727811, 0.633, 1.0};
   //~ GLfloat mat_shininess[] = {76.8};
   //~ glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_orange_diffuse);
   //~ glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
   //~ glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
   
   // verde
   //~ GLfloat amb[4] = {0.0215f, 0.1745f, 0.0215f, 0.55f};
   //~ GLfloat dif[4] = {0.07568f, 0.61424f, 0.07568f, 0.55f};
   //~ GLfloat spe[4] = {0.633f, 0.727811f, 0.633f, 0.55f};
   //~ GLfloat shi[1] = {76.8f};
   
   // rosado
   //~ GLfloat amb[4] = {0.0215f, 0.1745f, 0.0215f, 0.55f};
   //~ GLfloat dif[4] = {1-0.07568f, 1-0.61424f, 1-0.07568f, 0.55f};
   //~ GLfloat spe[4] = {0.633f, 0.727811f, 0.633f, 0.55f};
   //~ GLfloat shi[1] = {76.8f};
   
   // azul
   //~ GLfloat amb[4] = {0.33, 0.22, 0.03, 1.0};
   //~ GLfloat dif[4] = {1-0.78, 1-0.57, 1-0.11, 0.6};
   //~ GLfloat spe[4] = {0.99, 0.91, 0.81, 1.0};
   //~ GLfloat shi[1] = {27.8};
   
   // gold
   //~ GLfloat amb[4] = {0.24725f, 0.1995f, 0.0745f, 1.0};
   //~ GLfloat dif[4] = {0.75164f, 0.60648f, 0.22648f, 1.0};
   //~ GLfloat spe[4] = {0.928281f, 0.855802f, 0.666065f, 1.0};
   //~ GLfloat shi[1] = {51.2};
   
   //~ glMaterialfv(GL_FRONT, GL_AMBIENT,   amb);
   //~ glMaterialfv(GL_FRONT, GL_DIFFUSE,   dif);
   //~ glMaterialfv(GL_FRONT, GL_SPECULAR,  spe);
   //~ glMaterialfv(GL_FRONT, GL_SHININESS, shi);
   
   glClearColor(1,1,1,1);
   glColor4f(1,1,0,1);
   meshrendermode_mask = SMOOTH;
   sceneT.identity();
   
   std::time(&start);
   frame = 0;
   
   genWorldLimits();
   
   //~ texFloor = new Texture("icons/floor4.png", GL_TEXTURE_2D);
   //~ spriteTexCtlPoint = Texture("icons/redballT.png", GL_TEXTURE_2D);
   
   //~ kernel_xray.vertex_source("xray.vert");
   //~ kernel_xray.fragment_source("xray.frag");
   //~ kernel_xray.install(false);
   //~ kernel_xray.use(true);
   //~ kernel_xray.set_uniform("edgefalloff", 1.0f); 
   //~ kernel_xray.use(false);
   
   //~ setGLview();
   
}

/// It sets the projection/modelview matrices
void GLMeshModelFrame::setGLview() {
   
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   
   WorldWidth  = abs(maxcoo[0]-mincoo[0])*0.5;
   WorldHeight = abs(maxcoo[1]-mincoo[1])*0.5;
   
   objDistance = 0.8660*(maxcoo[1]-mincoo[1]); /// sqrt(3)/2 = 0.8660
   ////~ objDistance = WorldHeight;
   nearplane = objDistance - objDistance*0.5;
   farplane  = nearplane + 20.0;
   
   cameye[0] = camcenter[0];
   cameye[1] = camcenter[1];
   cameye[2] = maxcoo[2] + objDistance;
   
   if (isProjOrtho)
      glOrtho(-WorldWidth*zoomFactor,  WorldWidth*zoomFactor, 
              -WorldHeight*zoomFactor, WorldHeight*zoomFactor, 
               nearplane, farplane);
   else 
      gluPerspective(fovy*zoomFactor, (GLfloat)width()/(GLfloat)height(), nearplane, farplane);
   
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glViewport(0, 0, width(), height());
}

/// A simple resize callback.
/// @param width the width of the new window.
/// @param height the height of the new window.
void GLMeshModelFrame::resizeGL(int width, int height) {
   
   setGLview();
   arcball.setBounds(width, height);  //Update mouse bounds for the arcball
}

bool GLMeshModelFrame::loadShape(const QString& filename) {
   
   cout<<"::> ~loadShape()"<<endl;
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   
   Mesh *newmodel = 0;
   if (extension == "off")
      newmodel = Mesh::load_off(filename.toStdString().c_str());
   else
      return false;
   
   if (newmodel) {
      
      meshes.push_back(newmodel);
      newmodel = Mesh::load_off(filename.toStdString().c_str());
      meshesOri.push_back(newmodel);
      
      //~ /// OpenGL vertex arrays setup
      //~ glEnableClientState(GL_VERTEX_ARRAY);
      //~ glEnableClientState(GL_NORMAL_ARRAY);
      //~ typedef Mesh::Vertex Vertex;
      //~ Vertex_iterator vi = mesh->vertices_begin();
      //~ glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &(vi->point()[0]));
      //~ glNormalPointer(GL_FLOAT, sizeof(Vertex), &(vi->normal()[0]));
      
      cout<<"newmodel->center(): "<<newmodel->center()<<endl;
      cout<<"newmodel->radius(): "<<newmodel->radius()<<endl;
      genWorldLimits(newmodel->center(), newmodel->radius());
      setGLview();
      
      sceneT.identity();
      
      return true;
   }
   return false;
}

void GLMeshModelFrame::loadDeformedSkeleton(const QString& filename) {
   
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   
   assert(skelsTarget.size() == 1);
   
   if (extension == "ske") {
      if (skelsTarget[0] != 0) delete skelsTarget[0];
      skelsTarget[0] = GSkeleton::load(filename.toStdString().c_str());
   }
}

bool GLMeshModelFrame::loadSkeleton(const QString& filename) {
   
   cout<<"loadSkeleton: "<<filename.toStdString()<<endl;
   
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   
   GSkeleton *newskeleton = 0;
   if (extension == "asf") {
      SkeletonLoader sl;
      Skeleton *s = sl.load(filename.toStdString());
      
      newskeleton = new GSkeleton(*s);
      newskeleton->filename = filename.toStdString();
      delete s;
      
      if (newskeleton) {
         skelSource = newskeleton;
         
         for (int i=0; i<newskeleton->nBones(); ++i) {
            cout<<i<<" : "<<newskeleton->boneName(i)<<" :: "
                <<newskeleton->getBoneId(newskeleton->boneName(i))<<endl;
         }
         
         setGLview();
         sceneT.identity();
         return true;
      }
   }
   else if (extension == "ske") {
      GSkeleton *newSkel = 0;
      newSkel = GSkeleton::load(filename.toStdString().c_str());
      skelsTarget.push_back(newSkel);
      //~ newSkel->setJointPos(0, newSkel->getJointPos(0) + Vector3(-2,3,0));
      
      //~ newSkel->setJointPos(3, newSkel->getJointPos(3) + Vector3(-11,12,0));
      //~ newSkel->setJointPos(4, newSkel->getJointPos(4) + Vector3(-11,12,0));
      //~ newSkel->setJointPos(5, newSkel->getJointPos(5) + Vector3(-6,6,0));
      
      newSkel = GSkeleton::load(filename.toStdString().c_str());
      skelsTargetOri.push_back(newSkel);
      return true;
   }
   else
      return false;
   
   return false;
}

bool GLMeshModelFrame::loadMotion(const QString& filename) {
   
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   
   Motion *newmotion = 0;
   if (extension == "amc") {
      MotionLoader ml;
      newmotion = ml.load(filename.toStdString());
   }
   else
      return false;
   
   if (newmotion) {
      cout<<"newmotion->getMotionSize(): "<<newmotion->getMotionSize()<<endl;
      motions.push_back(newmotion);
      
      return true;
   }
   return false;
}

/// A simple opengl drawing callback
void GLMeshModelFrame::paintGL() {
   
   //~ setGLview();
   
   //~ glClearStencil(0x1);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   gluLookAt(cameye[0],cameye[1],cameye[2],camcenter[0],camcenter[1],camcenter[2],0,1,0);
   
   glMultMatrixf(&sceneT);
   
   // Draw the floor
   //~ if ((viewitems_mask & FLOOR) == FLOOR)
      //~ drawFloor();
   
   /*
   if ((viewitems_mask & FLOOR) == FLOOR) {
      glDisable(GL_LIGHTING);
      glPushMatrix();
      glMultMatrixf(shadowMatrix);
      glColor4f(0.4f, 0.4f, 0.4f, 1.0);
      if (activeobject == 0) glDrawModel(mesh, FLAT);
      if (activeobject == 1) glDrawSkeletonBones(skeletonSource, false);
      glPopMatrix();
      glEnable(GL_LIGHTING);
   }
   */
   
   
   // Draw the mesh
   if ((viewitems_mask & MODELDEF) == MODELDEF) {
      glLineWidth(1.0);
      (is_lightingOn)? glEnable(GL_LIGHTING):glDisable(GL_LIGHTING); 
      for (int i=0; i<meshes.size(); ++i) {
         glDrawModel(meshes[i], meshrendermode_mask);
         glPushAttrib(GL_LIGHTING_BIT);
         glDrawRiggingSkinning(meshes[i], skelsTarget[0], rigskin_mask);
         glPopAttrib();
      }
   }
   
   if (viewOriginalModel) {
      glPushAttrib(GL_LIGHTING_BIT);
      GLfloat mat_orange_diffuse[] = {0.0, 0.2, 0.8, 1.0};
      glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_orange_diffuse);
      for (int i=0; i<meshesOri.size(); ++i) {
         glDrawModel(meshesOri[i], meshrendermode_mask);
      }
      glPopAttrib();
   }
   
   
   //glColor3d(0.0, 0.0, 0.0);
   glColor3d(0.0, 0.0, 0.5);
   if ((viewitems_mask & SKELETONINI) == SKELETONINI) {
      //~ drawSkeleton(skeletonTargetOri);
      glDrawSkeletonBones(skelSource);
   }
   
   if ((viewitems_mask & SKELETONDEF) == SKELETONDEF) {
      for (int i=0; i<skelsTarget.size(); ++i) 
         drawSkeleton(skelsTarget[i]);
   }
   
   
   //~ if (activeobject) {
      //~ for (int i=0; i<skelsTarget.size(); ++i) 
         //~ drawSkeleton(skelsTarget[i]);
   //~ }
   
   //~ if ((viewitems_mask & RINGS) == RINGS)
      //~ drawIsoRings(mesh);
   //~ if ((viewitems_mask & SKELETONAUTO) == SKELETONAUTO) {
      //~ drawSkeleton(skeletonTarget);
      //~ drawSkeleton(skeletonTargetOri);
   //~ }
   
   //~ glDisable(GL_DEPTH_TEST);
   //~ glDisable(GL_LIGHTING);
   //~ glEnable(GL_BLEND);
   //~ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   //~ glActiveTexture(GL_TEXTURE0);
   //~ glEnable(GL_TEXTURE_2D);
   //~ glTexEnvf(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
   //~ spriteTexCtlPoint.bind();
   //~ glEnable(GL_POINT_SPRITE);
   //~ glPointSize(30);
   //~ glColor4f(1.0, 1.0, 1.0, 1.0);
      //~ glDrawConstraints(ctrlpoints);
   //~ glDisable(GL_POINT_SPRITE);
   //~ glDisable(GL_BLEND);
   //~ glDisable(GL_TEXTURE_2D);
   //~ glEnable(GL_DEPTH_TEST);
   
   if (viewBranches) {
      for (int i=0; i<matchers.size(); ++i) 
         glDrawBranches(&matchers[i]);
   }
   
   
   //~ if (idSelected<0)  setCursor(Qt::ArrowCursor);
   //~ else               setCursor(Qt::PointingHandCursor);
   
   // FPS info
   //~ if ((viewitems_mask & INFO) == INFO)
      //~ fpsInfo();
}

void GLMeshModelFrame::mousePressEvent(QMouseEvent *event) {
   
   xini = xlast=event->x();
   yini = ylast=event->y();
   cout<<"idSelected(pressEvent): "<<idSelected<<endl;
   
   //~ interactionMode = VIEW_MODE;
   
   if (event->button() == Qt::LeftButton) {
      
      list<uint> selected;
      cout<<"SELECT_JOINTS_MODE: "<<interactionMode<<endl;
      //~ makeCurrent();
      
      //~ if (idSelected == -1) { // select a joint
         //~ if (interactionMode == SELECT_CTRLPOINTS_MODE) {
            //~ selection.begin(event->x(),height()-event->y());
            //~ // glDrawConstraints(ctrlpoints);
            //~ selection.end(selected);
            
            //~ if (not selected.empty()) idSelected = selected.front();
         //~ } else {
            //~ selection.begin(event->x(),height()-event->y());
            //~ glDrawSkeletonJoints(skeletonSource);
            //~ selection.end(selected);
            
            //~ if (not selected.empty()) {
               //~ idSelected = selected.front();
               //~ interactionMode = DRAG_JOINTS_MODE;
            //~ }
         //~ }
         //~ cout<<"idSelected: on skeleton: "<<idSelected<<endl;
      //~ }
      //~ else {
         //~ cout<<"xxxxxxxx"<<idSelected<<endl;
         //~ selection.begin(event->x(),height()-event->y());
         //~ glDrawModelVertices(mesh);
         //~ selection.end(selected);
         //~ if (selected.size() > 0) {
            //~ cout<<"selected[0]: "<<selected.front()<<endl;
         //~ }
         
         //~ if (selected.size() > 1) {
            //~ Point3 c = mesh->getMidPointBetweenTwoFaces(selected.front(), selected.back());
            //~ if (interactionMode == SELECT_CTRLPOINTS_MODE) {
               //~ Facet_iterator fi = mesh->facets_begin();
               //~ advance(fi, selected.front());
               //~ Vertex_handle vh = fi->halfedge()->vertex();
               //~ seeds.push_back(vh);
            //~ }
            //~ else {
               //~ Vector3 t = c - skeletonSource->getJointPos(idSelected);
               //~ if (t*t == 0) return;
               //~ skeletonSource->translateJoint(idSelected, Vector3(t[0],t[1],t[2]));
               //~ interactionMode = VIEW_MODE;
            //~ }
            //~ idSelected = -1; // anyone is selected
         //~ }
      //~ }
      
      
      //~ selection.begin(event->x(),height()-event->y());
      //~ glDrawModelVertices(mesh);
      //~ selection.end(selected);
      //~ Vertex_iterator vi = mesh->vertices_begin();
      //~ cout<<"vi[].id(): "<<selected.size()<<" ::: "<<vi[selected.front()].id()<<endl;
      
      //~ cout<<"mesh idSelected: "<<idSelected<<" size: "<<selected.size()<<endl;
      glDraw();
      
   }
   else {
      xini=event->x(); yini=height()-event->y();
      if (event->button() == Qt::MidButton) {
         arcball.click(xini,yini);
      }
      else if (event->button() == Qt::RightButton) {
         grabLast = grabStart = screenToWorld(xini, yini);
      }
      sceneIniT = sceneT;
      cameyeI[0] = cameye[0];
      cameyeI[1] = cameye[1];
      cameyeI[2] = cameye[2];
      glDraw();
   }
}

void GLMeshModelFrame::mouseMoveEvent(QMouseEvent *event) {
   
   if (event->buttons() & Qt::LeftButton and (interactionMode == DRAG_JOINTS_MODE)) {
      /*
      int x=event->x(), y=height()-event->y();
      Vector3 t = getTranslationWorld(x,y,xlast,height()-ylast);
      if (t*t == 0)  return;
      skeletonSource->translateJoint(idSelected, Vector3(t[0],t[1],t[2]), editJointMode);
      
      xlast=event->x();
      ylast=event->y();
      
      event->accept();
      glDraw();
      */
   }
   else {
      if ((event->buttons() & Qt::MidButton)) { // rotations handler
         AMatrix<float> mT;
         arcball.drag(event->x(), height()-event->y(), &mT);
         sceneT = mT*sceneIniT;
         
         //~ AMatrix<float> mT;
         //~ arcball.drag(event->x(), height()-event->y(), &mT);
         //~ Vector3 v(cameyeI[0]-camcenter[0],cameyeI[1]-camcenter[1],cameyeI[2]-camcenter[2]);
         //~ float vp[3];
         //~ vp[0] = mT[0]*v[0] + mT[1]*v[1] + mT[2]*v[2];
         //~ vp[1] = mT[4]*v[0] + mT[5]*v[1] + mT[6]*v[2];
         //~ vp[2] = mT[8]*v[0] + mT[9]*v[1] + mT[10]*v[2];
         //~ cameye[0] = camcenter[0] + vp[0];
         //~ cameye[1] = camcenter[1] + vp[1];
         //~ cameye[2] = camcenter[2] + vp[2];
      }
      else if (event->buttons() & Qt::RightButton) { // translations handler
         Vector3 t = screenToWorld(xini, yini, event->x(), height()-event->y());
         AMatrix<float> mT;
         mT.identity();
         mT.translation(t[0], t[1], t[2]);
         sceneT = sceneIniT*mT;
      }
      glDraw();
   }
}

void GLMeshModelFrame::mouseReleaseEvent(QMouseEvent *event) {
   
   if (event->button() == Qt::LeftButton) {
      //~ if (interactionMode == SELECT_JOINTS_MODE) {
         if (xini!=event->x() or yini!=event->y()) {
            // there was a dragging
            idSelected = -1;
            interactionMode = VIEW_MODE;
         }
      //~ }
   }
   
   event->accept();
   glDraw();
}

void GLMeshModelFrame::keyPressEvent(QKeyEvent *event) {
   
   switch(event->key()) {
      case Qt::Key_Escape: exit(0);
      case Qt::Key_E: interactionMode = SELECT_JOINTS_MODE; break;
      case Qt::Key_V: interactionMode = VIEW_MODE; break;
      case Qt::Key_1: editJointMode = 'L'; break; // local translation
      case Qt::Key_2: editJointMode = 'T'; break; // hierarchical translation
      case Qt::Key_3: editJointMode = 'R'; break; // hierarchical rotation
      
      case Qt::Key_F1: activeobject = 0; break; // the models
      case Qt::Key_F2: activeobject = 1; break; // only the skeleton
      
      //~ case Qt::Key_F11: euclideanDist = true; break; // 
      //~ case Qt::Key_F12: euclideanDist = false; break; // 
         
      case Qt::Key_B: viewBranches = not viewBranches; break; // 
      case Qt::Key_O: viewOriginalModel = not viewOriginalModel; break;
         
      case Qt::Key_R: {
         if ((viewitems_mask & RINGS) == RINGS) viewItemOff(RINGS);
         else viewItemOn(RINGS);
         break; // 
      }
      case Qt::Key_S: {
         if ((viewitems_mask & SKELETONAUTO) == SKELETONAUTO) viewItemOff(SKELETONAUTO);
         else viewItemOn(SKELETONAUTO);
         break; // 
      }
      case Qt::Key_C: {
         
         ctrlpoints.clear();
         { /// homer mesh
            /*
         Vertex_iterator vi = mesh->vertices_begin();
         //~ ctrlpoints.push_back(vi[494].point(), vi[494].id()); // pe direito (front)
         //~ ctrlpoints.push_back(vi[4403].point(), vi[4403].id()); // pe direito (back)
         //~ ctrlpoints.push_back(vi[502].point(), vi[502].id()); // pe esquerdo (front)
         //~ ctrlpoints.push_back(vi[4287].point(), vi[4287].id()); // pe esquerdo (back)
         //~ ctrlpoints.push_back(vi[331].point(), vi[331].id()); // barriga
         //~ ctrlpoints.push_back(vi[3].point(), vi[3].id()); // mao direita
         //~ ctrlpoints.push_back(vi[7].point(), vi[7].id()); // mao esquerda
         
         ctrlpoints.push_back(vi[494].point()); // pe direito (front)
         ctrlpoints.push_back(vi[4403].point()); // pe direito (back)
         ctrlpoints.push_back(vi[510].point()); // pe esquerdo (front)
         ctrlpoints.push_back(vi[4287].point()); // pe esquerdo (back)
         ctrlpoints.push_back(vi[526].point()); // barriga
         ctrlpoints.push_back(vi[3].point()); // mao direita
         ctrlpoints.push_back(vi[7].point()); // mao esquerda
            */
         }
         
         /*
         { /// homer skeleton
         GSkeleton *s = skeletonTargetOri;
         ctrlpoints.push_back(s->getJointPos(3), 3); // pe direito (front)
         //~ ctrlpoints.push_back(s->getJointPos(4), 4); // pe direito (back)
         ctrlpoints.push_back(s->getJointPos(14), 14); // pe esquerdo (front)
         //~ ctrlpoints.push_back(s->getJointPos(13), 13); // pe esquerdo (back)
         ctrlpoints.push_back(s->getJointPos(6), 6); // barriga   
         //~ ctrlpoints.push_back(vi[3].point(), vi[3].id()); // mao direita
         //~ ctrlpoints.push_back(vi[7].point(), vi[7].id()); // mao esquerda
         }*/
         
         ctrlpointsIni = ctrlpoints;
         break; // 
      }
      case Qt::Key_A: {
         animNumber = 0;
         connect(animTimer, SIGNAL(timeout()), this, SLOT(animateDeformation()));
         animTimer->start(10);
         break;
      }
      case Qt::Key_Z: {
         disconnect(animTimer, 0, 0, 0);
         animTimer->stop();
         break;
      }
      case Qt::Key_F12: {
         sceneIniT = sceneT;
         connect(animTimer, SIGNAL(timeout()), this, SLOT(animateCamera()));
         animTimer->start(10);
         break;
      }
      case Qt::Key_F11: {
         disconnect(animTimer, 0, 0, 0);
         animTimer->stop();
         break;
      }
   }
   
   //~ cout<<"activeobject: "<<activeobject<<endl;
   glDraw();
}

void GLMeshModelFrame::wheelEvent(QWheelEvent *e) {
   
   //~ if (e->delta()>0) zoomFactor+= 0.1;
   //~ else              zoomFactor-= 0.1;
   //~ e->accept(); 
   //~ setGLview();
   //~ glDraw();
   
   if (e->delta()>0) zoomIn();
   else              zoomOut();
      
   e->accept();
}

void GLMeshModelFrame::fpsInfo() {
   
   //~ OpenGLFrame::fpsInfo();
   
   // Render info
   //~ if ((viewitems_mask & INFO) == INFO and mesh != 0) {
      //~ glDisable(GL_LIGHTING);
      //~ glDisable(GL_DEPTH_TEST);
      
      //~ QString strInfoNv=QString("vertices: %1").arg(mesh->nvertices());
      //~ QString strInfoNf=QString("faces: %1").arg(mesh->nfaces());
      
      //~ glColor4f(0.0, 0.0, 0.0, 1.0);
      //~ renderText(30,15, strInfoNv);
      //~ renderText(30,30, strInfoNf);
      
      //~ glEnable(GL_LIGHTING);
      //~ glEnable(GL_DEPTH_TEST);
   //~ }
}

void GLMeshModelFrame::buildAnimator() {
   
}

void GLMeshModelFrame::setSkeleton(GSkeleton *s) {
   
   //~ if (skeletonSource) delete skeletonSource;
   //~ skeletonSource = new GSkeleton(*s);
}

void GLMeshModelFrame::setMotion(Motion *m) { 
   
   //~ if (motion != 0) delete motion;
   //~ motion = m; 
}

void GLMeshModelFrame::buildCtrlPoints() {
   /*
   ctrlpoints.clear();
   Point3 root = skeleton_ori->getRootPosition();
   Point3 head = skeleton_ori->getJointPos(0);
   Point3 thorax = skeleton_ori->getJointPos(19);
   
   Point3 lfoot = skeleton_ori->getJointPos(3);
   Point3 rfoot = skeleton_ori->getJointPos(13);
   Point3 lwrist = skeleton_ori->getJointPos(10);
   Point3 rwrist = skeleton_ori->getJointPos(18);
   
   ctrlpoints.push_back(root, skeleton_ori->nBones());
   ctrlpoints.push_back(head, 0);
   ctrlpoints.push_back(thorax, 19);
   
   ctrlpoints.push_back(lfoot, 3);
   ctrlpoints.push_back(rfoot, 13);
   ctrlpoints.push_back(lwrist, 10);
   ctrlpoints.push_back(rwrist, 18);
   */
}

void GLMeshModelFrame::skeletonAlignment() {
   
   cout<<"skeletonAlignment() begin ... "<<endl;
   
   assert(skelSource != 0);
   assert(skelsTarget.size() > 0);
   assert(skelsTarget.size() > 0);
   
   for (int i=0; i<skelsTarget.size(); ++i) {
      SkeletonsMatcher match(skelSource, skelsTarget[i]);
      match.genBranchesRelation();
      matchers.push_back(match);
   }
   
   viewBranches = true;
   
   cout<<"skeletonAlignment() ... done! "<<endl;
   glDraw();
}

void GLMeshModelFrame::skeletonFitting() {
   
   cout<<"skeletonFitting() begin ..."<<endl;
   
   for (int i=0; i<matchers.size(); ++i) {
      matchers[i].fitBranches();
      skelsTarget[i]->export_to_ske("deformedSkel.ske");
   }
   viewBranches = false;
   
   assert(skelSource != 0);
   assert(skelsTarget.size() > 0);
   
   //~ for (int i=0; i<skelsTarget.size(); ++i) {
      //~ GAnimator ganim(skelsTarget[i], skelSource, motions[i]);
      //~ ganimators.push_back(ganim);
      //~ numberOfFrames = ganim.getMotionSize();
   //~ }
   
   
   //~ cout<<"artificial ... "<<endl;
   //~ ArtificialBone artBone(skeletonTargetOri, skeletonTarget);
   
   cout<<"skeletonFitting() end!!!!!!"<<endl;
   glDraw();
}

void GLMeshModelFrame::runMapSkeletonCtrlPoints() {
   
   cout<<endl<<endl<<endl;
   cout<<"runMapSkeletonCtrlPoints() ... begin ..."<<endl;
   /*
   if (skeletonSource != 0) delete skeletonSource;
   skeletonSource = new GSkeleton(*skeleton_ori);
   if (defMethod == "MLS")
      mapSkeletonCtrlPoints_MLS(skeleton_ori, skeletonSource, ctrlpoints, euclideanDist);
   else if (defMethod == "RBF")
      mapSkeletonCtrlPoints_RBF(skeleton_ori, skeletonSource, ctrlpoints);
   glDraw();
   */
   cout<<"runMapSkeletonCtrlPoints() end!!!!!!"<<endl;
}

void GLMeshModelFrame::meshDeformation() {
   
   if (meshes.empty() or meshesOri.empty()) return;
   if (skelSource==0 or skelsTarget.empty() or skelsTargetOri.empty()) return;
   
   assert(skelsTarget.size() == meshes.size());
   
   cout<<endl<<endl<<endl;
   cout<<"GLMeshModelFrame::meshDeformation() ... begin ...: "<<defMethod<<endl;
   Vector3 t(0.1,0.0,0.0);
   //~ Vector3 t(0.064,0.11,0.0);
   //~ Vector3 t(7,10,0);
   
   GSkeleton *skeletonSource = skelSource;
   for (int i=0; i<skelsTarget.size(); ++i) {
      GSkeleton *skeletonTarget = skelsTarget[i];
      GSkeleton *skeletonTargetOri = skelsTargetOri[i];
      
      Mesh *mesh = meshes[i];
      Mesh *meshOriginal = meshesOri[i];
      
      if (defMethod == "MLS_BLENDING") {
         if (not ctrlpoints.empty()) {
            //~ ctrlpoints.setConstraint(0, ctrlpoints.getConstraint(0) - t);
            //~ ctrlpoints.setConstraint(1, ctrlpoints.getConstraint(1) - t);
            ctrlpoints.setConstraint(1, ctrlpoints.getConstraint(1) + t);
            //~ ctrlpoints.setConstraint(3, ctrlpoints.getConstraint(3) + t);
            deformMesh_MLS_blending(meshOriginal, mesh, skeletonTargetOri, skeletonTarget, 
            ctrlpointsIni, ctrlpoints);
         } else {
            deformMesh_MLS_blending(meshOriginal, mesh, skeletonTargetOri, skeletonTarget);
         }
      }
      else if (defMethod == "MLS_CTRLPOINTS_EUCL") {
         if (not ctrlpoints.empty()) {
            //~ ctrlpoints.setConstraint(0, ctrlpoints.getConstraint(0) - t);
            //~ ctrlpoints.setConstraint(1, ctrlpoints.getConstraint(1) - t);
            ctrlpoints.setConstraint(2, ctrlpoints.getConstraint(2) + t);
            ctrlpoints.setConstraint(3, ctrlpoints.getConstraint(3) + t);
            deformMesh_MLS_controlPoints(meshOriginal, mesh, ctrlpointsIni, ctrlpoints);
         } else {
            mesh->isClustered = false;
            deformMesh_MLS_controlPoints(meshOriginal, mesh, skeletonTargetOri, skeletonTarget);
         }
      }
      else if (defMethod == "MLS_CTRLPOINTS_GEOD") {
         mesh->isClustered = meshOriginal->isClustered;
         deformMesh_MLS_controlPoints(meshOriginal, mesh, skeletonTargetOri, skeletonTarget);
      }
      else if (defMethod == "MLS_CTRLSEGMENTS_EUCL") {
         mesh->isClustered = false;
         deformMesh_MLS_controlSegments(meshOriginal, mesh, skeletonTargetOri, skeletonTarget);
      }
      else if (defMethod == "MLS_CTRLSEGMENTS_GEOD") {
         mesh->isClustered = meshOriginal->isClustered;
         deformMesh_MLS_controlSegments(meshOriginal, mesh, skeletonTargetOri, skeletonTarget);
      }
      else if (defMethod == "RBF_CLASSIC") {
         if (not ctrlpoints.empty()) {
            ctrlpoints.setConstraint(0, ctrlpoints.getConstraint(0) - t);
            ctrlpoints.setConstraint(1, ctrlpoints.getConstraint(1) - t);
            ctrlpoints.setConstraint(2, ctrlpoints.getConstraint(2) + t);
            ctrlpoints.setConstraint(3, ctrlpoints.getConstraint(3) + t);
            deformMesh_RBF_classic(meshOriginal, mesh, ctrlpointsIni, ctrlpoints);
         } else {
            deformMesh_RBF_classic(meshOriginal, mesh, skeletonTargetOri, skeletonTarget);
         }
      }
      else if (defMethod == "RBF_GEODESIC") {
         deformMesh_RBF_geodesic(meshOriginal, mesh, skeletonTargetOri, skeletonTarget);
      }
      else if (defMethod == "AFFINE_TRANSFORMATIONS")
         deformMesh_AT(meshOriginal, mesh, skeletonTargetOri, skeletonTarget);
      cout<<"GLMeshModelFrame::meshDeformation() ... begin ...: "<<defMethod<<endl;
      mesh->saveShapeOFF("deformedMesh.off");
   }
   
   /// animator da malha+skeleton
   for (int i=0; i<skelsTarget.size(); ++i) {
      GMeshAnimator anim(meshes[i], skelsTarget[i], skelSource, motions[i]);
      ganimatorsSM.push_back(anim);
      numberOfFrames = anim.getMotionSize();
   }
   
   cout<<"meshDeformation() ... end!!!!!!!!"<<endl;
   glDraw();
}

void GLMeshModelFrame::animateFrame(int animIndex) {
   
   if (animIndex<0) return;
   
   if(animIndex<0)   currentFrame = 0;
   else              currentFrame = animIndex;
   
   if (currentFrame == 0) {
      for (int i=0; i<ganimators.size(); ++i)
         ganimators[i].filterClear();
   }
   
   /// so esqueletos
   //~ for (int i=0; i<ganimators.size(); ++i)
      //~ ganimators[i].frame(currentFrame);
   
   /// esqueletos + meshes
   for (int i=0; i<ganimatorsSM.size(); ++i) {
      ganimatorsSM[i].frame(currentFrame); // com malha
      meshes[i]->saveShapeOFF("outputMesh.off");
      skelsTarget[i]->export_to_ske("outputSkel.ske");
   }
   
   glDraw();
}

void GLMeshModelFrame::resetOperations() {
   
   // reset the model
   //~ if (mesh != 0) delete mesh;
   //~ mesh = new Mesh(*meshOriginal);
   
   // reset the skeleton
   //~ if (skeletonTarget != 0) delete skeletonTarget;
   //~ skeletonTarget = new GSkeleton(*skeletonTargetOri);
   
   ctrlpoints = ctrlpointsIni;
   // reset the scene matrix
   //~ sceneT.identity();
   
   glDraw();
}

void GLMeshModelFrame::loadClosestJoint(const QString &filename) {
   
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   assert(extension == "cj");
   
   Mesh *mesh = meshes.back();
   Mesh *meshOriginal = meshesOri.back();
   assert(mesh != 0);
   assert(meshOriginal != 0);
   mesh->load_closestJoint(filename.toStdString().c_str());
   meshOriginal->load_closestJoint(filename.toStdString().c_str());
}

void GLMeshModelFrame::loadClosestBone(const QString &filename) {
   
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   assert(extension == "cb");
   
   Mesh *mesh = meshes.back();
   Mesh *meshOriginal = meshesOri.back();
   assert(mesh != 0);
   assert(meshOriginal != 0);
   mesh->load_closestBone(filename.toStdString().c_str());
   meshOriginal->load_closestBone(filename.toStdString().c_str());
}

void GLMeshModelFrame::loadSkinningOnJoints(const QString &filename) {
   
   Mesh *mesh = meshes.back();
   assert(mesh != 0);
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   assert(extension == "soj");
   mesh->load_skinningOnJoints(filename.toStdString().c_str());
}

void GLMeshModelFrame::loadSkinningOnBones(const QString &filename) {
   
   Mesh *mesh = meshes.back();
   assert(mesh != 0);
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   assert(extension == "sob");
   mesh->load_skinningOnBones(filename.toStdString().c_str());
   
   assert(not skelsTargetOri.empty());
   GSkeleton* skeletonTargetOri = skelsTargetOri.back(); // 
   assert(skeletonTargetOri != 0);
   Number t;
   Vertex_iterator vi = mesh->vertices_begin();
   while (vi != mesh->vertices_end()) {
      vi->ts.clear();
      for (int i=0; i<skeletonTargetOri->nBones(); ++i) {
         // Compute the closest point
         const Point3 &vp = vi->point();
         Number d = skeletonTargetOri->getDistance(vp, i, t);
         vi->ts.push_back(t); // the closest point
      }
      ++vi;
   }
   
   glDraw();
}

void GLMeshModelFrame::animateDeformation() {
   
   
   //~ TConstraints<Point3> ctrlpointsOri;
   //~ ctrlpointsOri = ctrlpoints;
   //~ ctrlpoints.setConstraint(0, ctrlpoints.getConstraint(0) - Vector3(1,0,0));
   //~ ctrlpoints.setConstraint(1, ctrlpoints.getConstraint(1) + Vector3(1,0,0));
   //~ deformMesh_MLS_controlPoints(meshOriginal, mesh, ctrlpointsOri, ctrlpoints);
   if (animNumber < 80) {
      ++animNumber;
      meshDeformation();
   }
   
   glDraw();
}

void GLMeshModelFrame::animateCamera() {
   
   //tx += 0.1;
   //angle = angle + 1.0;
   angle += 1.0;
   
   //~ sceneT.translation(tran[0], tran[1], tran[2]);
   //~ sceneT.rotation(angle, axis[0], axis[1], axis[2]);
   
   AMatrix<float> mT;
   mT.identity();
   mT.rotation(angle, axis[0], axis[1], axis[2]);
   sceneT = mT*sceneIniT;
   
   glDraw();
}

void GLMeshModelFrame::genWorldLimits(Point3 center, Number radius) {
   
   /*
   if (skeleton != 0) {
      //~ mincoo[0] = skeleton->minPoint[0]; mincoo[1] = skeleton->minPoint[1]; mincoo[2] = skeleton->minPoint[2];
      //~ maxcoo[0] = skeleton->maxPoint[0]; maxcoo[1] = skeleton->maxPoint[1]; maxcoo[2] = skeleton->maxPoint[2];
   }
   else {
      mincoo[0] = -1; mincoo[1] = -1; mincoo[2] = -1;
      maxcoo[0] =  1; maxcoo[1] =  1; maxcoo[2] =  1;
   }
   */
   
   
   mincoo[0] = -1; mincoo[1] = -1; mincoo[2] = -1;
   maxcoo[0] =  1; maxcoo[1] =  1; maxcoo[2] =  1;
   
   float xc = mincoo[0] + 0.5*(maxcoo[0]-mincoo[0]);
   float yc = mincoo[1] + 0.5*(maxcoo[1]-mincoo[1]);
   
   float WorldWidth  = abs(maxcoo[0]-mincoo[0]);
   float WorldHeight = abs(maxcoo[1]-mincoo[1]);
   
   if ((WorldHeight/WorldWidth) > ((float)height()/(float)width())) {
      WorldWidth = WorldHeight / ((float)height()/(float)width());
   }
   else if ((WorldHeight/WorldWidth) < ((float)height()/(float)width())) {
      WorldHeight = WorldWidth * ((float)height()/(float)width());
   }
   
   // world limits
   mincoo[0] = xc - 0.5*WorldWidth;
   mincoo[1] = yc - 0.5*WorldHeight;
   
   maxcoo[0] = xc + 0.5*WorldWidth;
   maxcoo[1] = yc + 0.5*WorldHeight;
   
   // center camera
   camcenter[0] = xc;
   camcenter[1] = yc;
   camcenter[2] = mincoo[2] + 0.5*(maxcoo[2]-mincoo[2]);
   
   
   /// //////////
   
   //~ // the camera points to
   //~ camcenter[0] = center.x();
   //~ camcenter[1] = center.y();
   //~ camcenter[2] = center.z();
   
   //~ // the camera position
   //~ cameye[0] = camcenter[0];
   //~ cameye[1] = camcenter[1];
   //~ cameye[2] = 2.0*radius;
   
   //~ nearplane = radius;
   //~ farplane = nearplane + 2.0*radius;
   //~ leftplane = -radius;
   //~ rightplane = radius;
   //~ bottomplane = -radius;
   //~ topplane = radius;
   
   //~ GLdouble aspect = (GLdouble)width()/(GLdouble)height();
   //~ if (aspect < 1.0) { // window taller than wide
      //~ bottomplane /= aspect;
      //~ topplane /= aspect;
   //~ }
   //~ else {
      //~ leftplane *= aspect;
      //~ rightplane *= aspect;
   //~ }
}

/// Returns the world coordinates of a point in screen space
Point3 GLMeshModelFrame::screenToWorld(int x, int y) {
   
   glGetIntegerv(GL_VIEWPORT, viewport);
   glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glGetDoublev(GL_MODELVIEW_MATRIX, mMtx);
   
   GLdouble xw, yw, zw;
   gluUnProject(x, y, 1.0, mMtx, pMtx, viewport, &xw, &yw, &zw);
   return Point3(xw, yw, zw);
}

/// Returns the translation vector in world coordinates of two points in screen space
Vector3 GLMeshModelFrame::screenToWorld(int xi, int yi, int xe, int ye) {
   
   glGetIntegerv(GL_VIEWPORT, viewport);
   //~ glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glGetDoublev(GL_MODELVIEW_MATRIX, mMtx);
   
   /// Compute the world coordinates using the orthographic projection
   glMatrixMode(GL_PROJECTION);
   glPushMatrix();
   glLoadIdentity();
   glOrtho(-WorldWidth*zoomFactor,  WorldWidth*zoomFactor, 
           -WorldHeight*zoomFactor, WorldHeight*zoomFactor, 
            nearplane, farplane);
   glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glPopMatrix();
   
   GLdouble ws[3], we[3]; // project on the near plane
   gluUnProject(xi, yi, 0, mMtx, pMtx, viewport, &ws[0], &ws[1], &ws[2]);
   gluUnProject(xe, ye, 0, mMtx, pMtx, viewport, &we[0], &we[1], &we[2]);
   
   return Vector3(we[0]-ws[0], we[1]-ws[1], we[2]-ws[2]);
}

Vector3 GLMeshModelFrame::getTranslationWorld(int x, int y, int xlastt, int ylastt) {
   
   glGetDoublev(GL_MODELVIEW_MATRIX, mMtx);
   glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glGetIntegerv(GL_VIEWPORT, viewport);
   
   GLdouble m[]={1,0,0,0,
              0,1,0,0,
              0,0,1,0,
              0,0,0,1};
   
   double wbx, wby, wbz, wex, wey, wez;
   gluUnProject(xlastt, ylastt, 0.0, mMtx, pMtx, viewport, &wbx, &wby, &wbz);
   gluUnProject(x, y, 0.0, mMtx, pMtx, viewport, &wex, &wey, &wez);
   return Vector3(wex-wbx, wey-wby, wez-wbz);
   
   //~ gluUnProject(xlast, ylast, 0.0, m, pMtx, viewport, &wbx, &wby, &wbz);
   //~ gluUnProject(x, y, 0.0, m, pMtx, viewport, &wex, &wey, &wez);
   //~ return Vector(wex-wbx, wey-wby, 0);
}

/// Implements the "zoomin" operation
void GLMeshModelFrame::zoomIn () {
   
   AMatrix<float> mT;
   mT.identity();
   mT.scaling(1.2);
   sceneT = mT*sceneT;
   
   glDraw();
}

/// Implements the "zoomout" operation
void GLMeshModelFrame::zoomOut () {
   
   AMatrix<float> mT;
   mT.identity();
   mT.scaling(1.0/1.2);
   sceneT = mT*sceneT;
   
   glDraw();
}

/// Implements the "zoomall" operation
void GLMeshModelFrame::zoomAll () {
   
   sceneT.identity();
   glDraw();
}

void GLMeshModelFrame::createShadowProjectionMatrix(float *l, float *e, float *n) {
   
   // These are c and d (corresponding to the tutorial)
   float d = n[0]*l[0] + n[1]*l[1] + n[2]*l[2];
   float c = e[0]*n[0] + e[1]*n[1] + e[2]*n[2] - d;
   
   // Create the matrix. OpenGL uses column by column ordering
   shadowMatrix[0]  = l[0]*n[0]+c;
   shadowMatrix[4]  = n[1]*l[0];
   shadowMatrix[8]  = n[2]*l[0];
   shadowMatrix[12] = -l[0]*c-l[0]*d;
  
   shadowMatrix[1]  = n[0]*l[1];
   shadowMatrix[5]  = l[1]*n[1]+c;
   shadowMatrix[9]  = n[2]*l[1];
   shadowMatrix[13] = -l[1]*c-l[1]*d;
  
   shadowMatrix[2]  = n[0]*l[2];
   shadowMatrix[6]  = n[1]*l[2];
   shadowMatrix[10] = l[2]*n[2]+c;
   shadowMatrix[14] = -l[2]*c-l[2]*d;
  
   shadowMatrix[3]  = n[0];
   shadowMatrix[7]  = n[1];
   shadowMatrix[11] = n[2];
   shadowMatrix[15] = -d;
}
